package cn.lili.controller.auth;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.lili.common.enums.ResultCode;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.security.enums.SceneEnums;
import cn.lili.common.security.token.Token;
import cn.lili.common.utils.ResultUtil;
import cn.lili.common.utils.StringUtils;
import cn.lili.common.vo.ResultMessage;
import cn.lili.modules.auth.service.AuthService;
import cn.lili.modules.auth.service.QrCodeLoginService;
import cn.lili.modules.constant.CommonConstant;
import cn.lili.modules.member.client.UserClient;
import cn.lili.modules.member.entity.dos.User;
import cn.lili.modules.member.entity.dto.UserInfoDTO;
import cn.lili.modules.member.entity.dto.UserLoginDTO;
import cn.lili.modules.member.entity.enums.QRCodeLoginSessionStatusEnum;
import cn.lili.modules.member.entity.vo.QRLoginResultVo;
import cn.lili.modules.member.entity.vo.UserVO;
import cn.lili.modules.model.ScanCodeModal;
import cn.lili.modules.operationcenter.client.OperationCenterClient;
import cn.lili.modules.operationcenter.entity.dos.OperationCenter;
import cn.lili.modules.operationcenter.entity.enums.OperationCenterStatusEnum;
import cn.lili.modules.service.TicketService;
import cn.lili.modules.store.client.StoreClient;
import cn.lili.modules.store.entity.dos.Store;
import cn.lili.modules.store.entity.enums.StoreStatusEnum;
import cn.lili.modules.system.client.SmsClient;
import cn.lili.modules.system.entity.params.SmsParams;
import cn.lili.modules.token.TokenGenerate;
import cn.lili.modules.urbansubstation.client.UrbanSubstationClient;
import cn.lili.modules.urbansubstation.entity.dos.UrbanSubstation;
import cn.lili.modules.urbansubstation.entity.enums.UrbanSubstationStatusEnum;
import cn.lili.modules.verification.entity.enums.VerificationEnums;
import cn.lili.modules.verification.service.VerificationService;
import com.alibaba.fastjson.JSON;
import com.pojos.OpenAppUser;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.async.DeferredResult;

import javax.validation.constraints.NotNull;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

/**
 * 认证接口
 *
 * @author liushuai(liushuai711 @ gmail.com)
 * @version v4.0
 * @Description:
 * @since 2022/8/23 16:33
 */
@RestController
@Api(tags = "认证接口")
@RequestMapping("/auth")
@RequiredArgsConstructor
@Slf4j
public class AuthController {


    private final AuthService authService;

    private final UserClient userClient;

    private final StoreClient storeClient;

    private final SmsClient smsClient;

    private final VerificationService verificationService;

    private final TokenGenerate tokenGenerate;

    private final UrbanSubstationClient urbanSubstationClient;

    private final QrCodeLoginService qrCodeLoginService;

    private final TicketService ticketService;

    private final OperationCenterClient operationCenterClient;

    @Value("${fw.server}")
    String fwServer;

    @ApiOperation(value = "登录接口")
    @ApiImplicitParams({@ApiImplicitParam(name = "username", value = "用户名", required = true, paramType = "query"),
            @ApiImplicitParam(name = "password", value = "密码", required = true, paramType = "query"),
            @ApiImplicitParam(name = "scene", value = "场景", required = true, paramType = "header")})
    @PostMapping("/login")
    public ResultMessage<Object> userLogin(@NotNull(message = "username cannot be empty") String username,
                                           @NotNull(message = "password cannot be empty") String password,
                                           @NotNull(message = "scene cannot be empty") @RequestHeader String scene,
                                           @RequestHeader String uuid) {

//        verificationService.check(uuid, SceneEnums.getScene(scene), VerificationEnums.LOGIN);

        UserLoginDTO userLoginDTO = new UserLoginDTO();
        userLoginDTO.setPassword(password);
        userLoginDTO.setUsername(username);
        userLoginDTO.setScene(SceneEnums.getScene(scene));
        User user = userClient.queryUser(userLoginDTO);
        if (SceneEnums.STORE.equals(userLoginDTO.getScene()) || SceneEnums.SUPPLIER.equals(userLoginDTO.getScene())) {
            Store store = storeClient.getStore(user.getExtendId());
            if (store == null || !StoreStatusEnum.OPEN.name().equals(store.getStoreStatus())) {
                throw new ServiceException(ResultCode.STORE_CLOSE_ERROR);
            }
        } else if (SceneEnums.URBAN_SUBSTATION.equals(userLoginDTO.getScene())) {
            UrbanSubstation urbanSubstation = urbanSubstationClient.getUrbanSubstation(user.getId());
            if (urbanSubstation == null || !UrbanSubstationStatusEnum.OPEN.name().equals(urbanSubstation.getStatus())) {
                // 子用户登录
                UrbanSubstation childUrban = urbanSubstationClient.getById(user.getExtendId());
                if (childUrban == null || !UrbanSubstationStatusEnum.OPEN.name().equals(childUrban.getStatus())) {
                    throw new ServiceException(ResultCode.URBAN_CLOSE_ERROR);
                }
            }
        } else if (SceneEnums.OPERATION_CENTER.equals(userLoginDTO.getScene())) {
            OperationCenter operationCenter = operationCenterClient.getOperationCenter(user.getId());
            if (operationCenter == null || !OperationCenterStatusEnum.OPEN.name().equals(operationCenter.getStatus())) {
                // 子用户登录
                OperationCenter childOc = operationCenterClient.getById(user.getExtendId());
                if (childOc == null || !OperationCenterStatusEnum.OPEN.name().equals(childOc.getStatus())) {
                    throw new ServiceException(ResultCode.OPERATION_CENTER_CLOSE_ERROR);
                }
            }
        }
        //城市分站和供应商登录未绑定飞语，则跳转到扫码绑定页面
        if (SceneEnums.URBAN_SUBSTATION.equals(userLoginDTO.getScene()) || SceneEnums.SUPPLIER.equals(userLoginDTO.getScene())) {
            //用户是否绑定飞语
//            ResultMessage<Object> isBindMessage = qrCodeLoginService.userIsBind(user);
//            if (StringUtils.isEmpty(user.getOpenId())) {
//                return ResultUtil.error(500, "账号未绑定飞语，请跳转到扫码绑定页面", user.getId());
//            }
        }
        return ResultUtil.data(tokenGenerate.generateToken(user, false));
    }

    @ApiOperation(value = "获取用户信息")
    @GetMapping("/info")
    public ResultMessage<Object> info() {
        UserVO info = this.authService.info();
        return ResultUtil.data(info);
    }

    @ApiOperation(value = "注销接口")
    @PostMapping("/logout")
    @ApiImplicitParam(name = "scene", value = "场景", required = true, paramType = "header")
    public ResultMessage<Object> logout(@NotNull(message = "scene cannot be empty") @RequestHeader String scene) {
        this.authService.logout(SceneEnums.getScene(scene));
        return ResultUtil.success();
    }

    @ApiOperation(value = "短信登录接口")
    @ApiImplicitParams({@ApiImplicitParam(name = "mobile", value = "手机号", required = true, paramType = "query"),
            @ApiImplicitParam(name = "code", value = "验证码", required = true, paramType = "query"),
            @ApiImplicitParam(name = "scene", value = "场景", required = true, paramType = "header")})
    @PostMapping("/smsLogin")
    public ResultMessage<Token> smsLogin(@NotNull(message = "mobile cannot be empty") @RequestParam String mobile,
                                         @NotNull(message = "code cannot be empty") @RequestParam String code,
                                         @NotNull(message = "scene cannot be empty") @RequestHeader String scene,
                                         @RequestHeader String uuid) {
        //短信验证通过
        if (smsClient.verifyCode(new SmsParams(mobile, VerificationEnums.LOGIN, uuid, code,
                SceneEnums.getScene(scene)))) {
            User user = userClient.mobilePhoneLogin(mobile, SceneEnums.getScene(scene));
            if (SceneEnums.SUPPLIER.name().equals(scene)) {
                if (CommonConstant.AUDIT_STATUS_00.equals(user.getAuditStatus())) {
                    return ResultUtil.error(500, "分销商未审核");
                }
                if (CommonConstant.AUDIT_STATUS_02.equals(user.getAuditStatus())) {
                    return ResultUtil.error(500, "分销商审核未通过");
                }
            }
            return ResultUtil.data(tokenGenerate.generateToken(user, false));
        } else {
            throw new ServiceException(ResultCode.VERIFICATION_SMS_CHECKED_ERROR);
        }
    }



    /**
     * 创建二维码
     *
     * @param scene       应用场景
     * @return 二维码图片
     */
    @RequestMapping("/api/createQrCode")
    public ResultMessage<Object> createQrCode(String scene, String userId) {
        return qrCodeLoginService.createQrCode( scene,  userId);
    }


    /**
     * 创建二维码
     * @param code 二维码id
     * @return 二维码图片
     */
    @RequestMapping("/api/checkCode")
    public ResultMessage<Object> checkCode(String code) {
        return qrCodeLoginService.checkCode(code);
    }



    @ApiOperation(value = "移动端登录接口")
    @ApiImplicitParams({@ApiImplicitParam(name = "sessionId", value = "飞语sessionId", required = true, paramType = "query"),
            @ApiImplicitParam(name = "scene", value = "场景", required = true, paramType = "query")})
    @RequestMapping("/appLogin")
    ResultMessage<Object> appLogin(@RequestParam String sessionId,@RequestParam String scene) {
        return qrCodeLoginService.appLogin(sessionId, scene);
    }
//    /**
//     * 二维码过期
//     */
//    @RequestMapping("/api/getCodeExpire")
//    public ResultMessage<Object> getCodeExpire(String key) {
//        return qrCodeLoginService.getCodeExpire(key);
//    }

//    /**
//     * 平台用户与飞语进行绑定
//     *
//     * @param userId    用户id
//     * @param sessionId 飞语临时id
//     * @param key       二维码id
//     * @return 是否绑定成功
//     */
//    @PostMapping("/api/user/bind/fy")
//    public ResultMessage<Object> userBindFy(@RequestParam String userId, String sessionId, String key) {
//        return qrCodeLoginService.userBindFy(userId, sessionId, key);
//    }
//
//    /**
//     * 是否绑定用户
//     * @param userId 用户Id
//     * @return 是否绑定
//     */
//    @GetMapping("/api/isBind")
//    public ResultMessage<Object> isBind(@RequestParam String userId) {
//        User user = new User();
//        user.setId(userId);
//        return qrCodeLoginService.userIsBind(user);
//    }
//
//    /**
//     * 移动端登录，自动创建账号，并授权。
//     *
//     * @param sessionId 飞语临时Id
//     * @param scene     应用场景
//     * @return token
//     */
//    @PostMapping("/api/app/login")
//    public ResultMessage<Object> appLogin(@RequestParam String sessionId, @RequestParam String scene) {
//        return qrCodeLoginService.appLogin(sessionId, scene);
//    }

    @ApiOperation(value = "注册用户")
    @ApiImplicitParams({@ApiImplicitParam(name = "username", value = "用户名", required = true, paramType = "query"),
            @ApiImplicitParam(name = "password", value = "密码", required = true, paramType = "query"),
            @ApiImplicitParam(name = "mobilePhone", value = "手机号", required = true, paramType = "query"),
            @ApiImplicitParam(name = "scene", value = "场景", required = true, paramType = "header"),
            @ApiImplicitParam(name = "code", value = "验证码", required = true, paramType = "query")})
    @PostMapping("/register")
    public ResultMessage<Object> register(@NotNull(message = "username cannot be empty") @RequestParam String username, @NotNull(message = "password cannot be empty") @RequestParam String password, @NotNull(message = "mobile cannot be empty") @RequestParam String mobilePhone, @NotNull(message = "scene cannot be empty") @RequestHeader String scene, @NotNull(message = "code cannot be empty") @RequestParam String code, @RequestHeader String uuid) {
        if (smsClient.verifyCode(new SmsParams(mobilePhone, VerificationEnums.REGISTER, uuid, code,
                SceneEnums.getScene(scene)))) {
            UserInfoDTO userInfoDTO =
                    UserInfoDTO.builder().username(username).password(new BCryptPasswordEncoder().encode(password)).mobile(mobilePhone).scene(SceneEnums.valueOf(scene)).build();
            //注册判定
            SceneEnums.canRegister(userInfoDTO.getScene());
            return ResultUtil.data(userClient.register(userInfoDTO));
        } else {
            throw new ServiceException(ResultCode.VERIFICATION_SMS_CHECKED_ERROR);
        }
    }


    @ApiOperation(value = "重置密码1-校验短信是否正确，来标记是否可以充值密码")
    @ApiImplicitParams({@ApiImplicitParam(name = "mobile", value = "手机号", required = true, paramType = "query"),
            @ApiImplicitParam(name = "password", value = "是否保存登录", required = true, paramType = "query"),
            @ApiImplicitParam(name = "scene", value = "场景", required = true, paramType = "header")})
    @PostMapping("/reset-mobile")
    public ResultMessage<User> resetPasswordSign(@NotNull(message = "mobile cannot be empty") @RequestParam String mobile, @NotNull(message = "code cannot be empty") @RequestParam String code, @NotNull(message = "scene cannot be empty") @RequestHeader String scene, @RequestHeader String uuid) {
        //校验短信验证码是否正确
        if (smsClient.verifyCode(new SmsParams(mobile, VerificationEnums.FIND_USER, uuid, code,
                SceneEnums.getScene(scene)))) {
            //校验是否通过手机号可获取会员,存在则将会员信息存入缓存，有效时间3分钟
            userClient.resetPasswordSign(uuid, mobile, SceneEnums.getScene(scene));
            return ResultUtil.success();
        } else {
            throw new ServiceException(ResultCode.VERIFICATION_SMS_CHECKED_ERROR);
        }
    }

    @ApiOperation(value = "重置密码2-设置新密码")
    @ApiImplicitParams({@ApiImplicitParam(name = "password", value = "是否保存登录", required = true, paramType = "query"),
            @ApiImplicitParam(name = "scene", value = "场景", required = true, paramType = "header"),
            @ApiImplicitParam(name = "uuid", value = "uuid", required = true, paramType = "query")})
    @PostMapping("/reset-password")
    public ResultMessage<Object> resetPasswordByMobile(@NotNull(message = "password cannot be empty") @RequestParam String password, @NotNull(message = "scene cannot be empty") @RequestHeader String scene, @RequestHeader String uuid) {
        Boolean result = userClient.resetPasswordByMobile(uuid, password, SceneEnums.getScene(scene));
        return ResultUtil.data(result);
    }


    @ApiOperation(value = "刷新token")
    @GetMapping("/refresh/{refreshToken}")
    @ApiImplicitParam(name = "scene", value = "场景", required = true)
    public ResultMessage<Object> refreshToken(@PathVariable @NotNull(message = "refreshToken cannot be empty") String refreshToken) {
        return ResultUtil.data(this.tokenGenerate.refreshToken(refreshToken));
    }

    @ApiOperation(value = "web-获取登录二维码")
    @PostMapping(value = "/pc-session", produces = "application/json;charset=UTF-8")
    public ResultMessage<Object> createPcSession() {
        return ResultUtil.data(this.tokenGenerate.createPcSession());
    }


    /**
     * 长轮询：参考nacos
     *
     * @param token
     * @param beforeSessionStatus 上次记录的session状态
     * @return
     */
    @ApiOperation(value = "web-二维码登录")
    @PostMapping(value = "/session-login/{token}", produces = "application/json;charset=UTF-8")
    public Object loginWithSession(@PathVariable("token") String token, Integer beforeSessionStatus) {
        log.info("receive login with session key {}", token);
        ResponseEntity<ResultMessage<Object>> timeoutResponseEntity =
                new ResponseEntity<>(ResultUtil.error(ResultCode.ERROR), HttpStatus.OK);
        int timeoutSecond = 20;
        DeferredResult<ResponseEntity<Object>> deferredResult = new DeferredResult<>(timeoutSecond * 1000L, timeoutResponseEntity);
        CompletableFuture.runAsync(() -> {
            try {
                int i = 0;
                while (i < timeoutSecond) {
                    QRLoginResultVo queryResult = tokenGenerate.loginWithSession(token);
                    int status = queryResult.getStatus();
                    if (status == beforeSessionStatus
                            && (QRCodeLoginSessionStatusEnum.WAIT_SCANNING.getCode() == status
                            || QRCodeLoginSessionStatusEnum.SCANNING.getCode() == status)) {
                        //睡眠一秒种，继续等待结果
                        TimeUnit.SECONDS.sleep(1);
                    } else {
                        deferredResult.setResult(new ResponseEntity<>(ResultUtil.data(queryResult), HttpStatus.OK));
                        break;
                    }
                    i++;
                }
            } catch (Exception e) {
                log.error("获取登录状态异常，", e);
                deferredResult.setResult(new ResponseEntity<>(ResultUtil.error(ResultCode.ERROR), HttpStatus.OK));
                Thread.currentThread().interrupt();
            }
        });
        return deferredResult;
    }

    @ApiOperation(value = "app扫码")
    @PostMapping(value = "/app-scanner", produces = "application/json;charset=UTF-8")
    public ResultMessage<Object> appScanner(String token) {
        return ResultUtil.data(tokenGenerate.appScanner(token));
    }


    @ApiOperation(value = "app扫码-登录确认：同意/拒绝")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "token", value = "sessionToken", required = true, paramType = "query"),
            @ApiImplicitParam(name = "code", value = "操作：0拒绝登录，1同意登录", required = true, paramType = "query")
    })
    @PostMapping(value = "/app-confirm", produces = "application/json;charset=UTF-8")
    public ResultMessage<Object> appSConfirm(String token, Integer code) {
        boolean flag = tokenGenerate.appSConfirm(token, code);
        return flag ? ResultUtil.success() : ResultUtil.error(ResultCode.ERROR);
    }

}
